package com.ww.microrpc.netty.handler.server;

import com.ww.microrpc.executer.MethodExecutors;
import com.ww.microrpc.netty.protocol.ResponseMessage;
import com.ww.microrpc.netty.protocol.BeatMessage;
import com.ww.microrpc.netty.protocol.RequestMessage;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * RPC Handler（RPC request processor）
 *
 * @author ww
 */
public class RequestMessageHandler extends SimpleChannelInboundHandler<RequestMessage> {

    private static final Logger logger = LoggerFactory.getLogger(RequestMessageHandler.class);

    private final ThreadPoolExecutor serverHandlerPool;

    private MethodExecutors methodExecutors;

    public RequestMessageHandler(final ThreadPoolExecutor threadPoolExecutor, MethodExecutors methodExecutors) {
        this.serverHandlerPool = threadPoolExecutor;
        this.methodExecutors = methodExecutors;
    }

    @Override
    public void channelRead0(final ChannelHandlerContext ctx, final RequestMessage request) {
        // filter beat ping
        if (BeatMessage.BEAT_ID == request.getRequestId()) {
            logger.info("Server read heartbeat ping");
            return;
        }

        serverHandlerPool.execute(new Runnable() {
            @Override
            public void run() {
                logger.info("Receive request id: {} ", request.getRequestId());
                ResponseMessage response = new ResponseMessage();
                response.setRequestId(request.getRequestId());
                try {
                    Object result = handle(request);
                    response.setResult(result);
                } catch (Throwable t) {
                    response.setErrorMessage(t.toString());
                    logger.error("RPC Server handle request error", t);
                }
                ctx.writeAndFlush(response).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture channelFuture) throws Exception {
                        logger.info("Send response for request id: {}", request.getRequestId());
                    }
                });
            }
        });
    }

    private Object handle(RequestMessage request) throws Throwable {
        String className = request.getClassName();
//        String version = request.getVersion();
//        String serviceKey = ServiceUtil.makeServiceKey(className, version);
//        Object serviceBean = handlerMap.get(serviceKey);
//        if (serviceBean == null) {
//            logger.error("Can not find service implement with interface name: {} and version: {}", className, version);
//            return null;
//        }

//        Class<?> serviceClass = serviceBean.getClass();
//        String methodName = request.getMethodName();
//        Class<?>[] parameterTypes = request.getParameterTypes();
//        Object[] parameters = request.getParameters();
//
//        logger.debug(serviceClass.getName());
//        logger.debug(methodName);
//        for (int i = 0; i < parameterTypes.length; ++i) {
//            logger.debug(parameterTypes[i].getName());
//        }
//        for (int i = 0; i < parameters.length; ++i) {
//            logger.debug(parameters[i].toString());
//        }

        // JDK reflect
//        Method method = serviceClass.getMethod(methodName, parameterTypes);
//        method.setAccessible(true);
//        return method.invoke(serviceBean, parameters);

        // Cglib reflect
//        FastClass serviceFastClass = FastClass.create(serviceClass);
//        FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes);
//        return serviceFastMethod.invoke(serviceBean, parameters);
        // for higher-performance
//        int methodIndex = serviceFastClass.getIndex(methodName, parameterTypes);
//        return serviceFastClass.invoke(methodIndex, serviceBean, parameters);

        return methodExecutors.invoke(request);
    }
}
